﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SoftwareConsulting.BI11.Studio.InternalServices;
using SoftwareConsulting.BI11.Studio.Core.Project;
using SoftwareConsulting.BI11.Studio.Core.Project.Metadata;
using SoftwareConsulting.BI11.Studio.Core.Project.SituationsHierarchy;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.Extensions;
using SoftwareConsulting.BI11.NumericalMethods.Geometry;
using SoftwareConsulting.BI11.Controls.Metadata;

namespace SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.Models
{
    /// <summary>
    /// Класс диалога редактирования модели
    /// </summary>
    public partial class ModelEditorDialog : RestorableForm
    {
        private sealed class ModelInfo
        {
            private ModelType _type;
            private MissingDataMethod _missingDataMethod;
            private TimeSeriesWorld _outseries;
            private List<TimeSeriesWorld> _inseries;

            public ModelInfo()
            {
                _type = ModelType.None;
                _missingDataMethod = MissingDataMethod.None;
            }

            public ModelType ModelType
            {
                get
                {
                    return _type;
                }
                set
                {
                    _type = value;
                }
            }

            public MissingDataMethod GapsFillingMethod
            {
                get
                {
                    return _missingDataMethod;
                }
                set
                {
                    _missingDataMethod = value;
                }
            }

            public TimeSeriesWorld OutSeriesWorld
            {
                get
                {
                    return _outseries;
                }
                set
                {
                    _outseries = value;
                }
            }

            public List<TimeSeriesWorld> InSeriesWorld
            {
                get
                {
                    return _inseries;
                }
                set
                {
                    _inseries = value;
                }
            }
        }

        private ModelInfo _modelinfo;
        private bool _starting; //флаг состояния загрузки формы
        private IModel _editingModel; //редактируемая модель
        private ProjectDescriptor _project;

        /// <summary>
        /// Чтение всех миров
        /// </summary>
        private void ReadWorlds(ComboBox box)
        {
            INamedHierarchy<INamed> worlds = _project.RepositoryLink.WorldsDimension.Items;
            box.Items.Clear();            
            for (int i = 0; i < worlds.ItemsCount; i++)
            {
                box.Items.Add(worlds.GetItemByPosition(i).Name);                
            }                        
        }

        /// <summary>
        /// Чтение всех миров
        /// </summary>
        private void ReadWorlds()
        {
            ReadWorlds(InSeriesWorldBox);
            ReadWorlds(OutSeriesWorldBox);            
        }

        /// <summary>
        /// Инициализация диалога
        /// </summary>
        private void Initialize()
        {
            this.SetObjectViewerCaption(this.Text, _editingModel);
            this.Icon = MetadataObjectKind.Model.GetIcon();
            _starting = true;
            //строим info
            _modelinfo = new ModelInfo();
            _modelinfo.InSeriesWorld = new List<TimeSeriesWorld>();            
            //чтение всех миров
            ReadWorlds();            
            //составляем список всех рядов
            List<string> allserieslabels = new List<string>();
            foreach(MetadataObjectDescriptor info in _project.RepositoryLink.GetObjectsDescriptors(MetadataObjectKind.TimeSeries))
            {
                allserieslabels.Add(info.Label);
            }
            AllSeriesBox.Items.AddRange(allserieslabels.ToArray());
            ListBox.ObjectCollection rows = InSeriesBox.Items;
            rows.Clear();
            for (int i = 0; i < _editingModel.GetInSeriesCount(); i++)
            {
                ITimeSeries inser = _editingModel.GetInSeriesByIndex(i);                
                rows.Add(inser.Label);
                _modelinfo.InSeriesWorld.Add(
                    new TimeSeriesWorld(_editingModel.GetInSeriesWorldKeyByIndex(i), inser)
                );
            }
            OutSeriesBox.Items.AddRange(allserieslabels.ToArray());
            while (true)
            {
                if (_editingModel.GetOutSeries() == null)
                {
                    if (OutSeriesBox.Items.Count == 0)
                    {
                        OutSeriesBox.Enabled = false;
                        OutSeriesWorldBox.Enabled = false;
                        break;
                    }
                    _modelinfo.OutSeriesWorld = new TimeSeriesWorld(
                        _project.RepositoryLink.GetOrSetWorldKeyByName(OutSeriesWorldBox.Items[0] as string),
                        _project.RepositoryLink.GetObjectDescriptorByLabel(OutSeriesBox.Items[0] as string, MetadataObjectKind.TimeSeries).Bind() as ITimeSeries);
                }
                else
                {
                    _modelinfo.OutSeriesWorld = new TimeSeriesWorld(
                        _editingModel.GetOutSeriesWorldKey(), _editingModel.GetOutSeries()
                        );
                }
                OutSeriesBox.SelectedItem = _modelinfo.OutSeriesWorld.TimeSeries.Label;
                OutSeriesWorldBox.SelectedItem = _project.RepositoryLink.GetWorldNameByKey(_modelinfo.OutSeriesWorld.WorldKey);                
                break;
            }
            ComboBox.ObjectCollection items = KindBox.Items;
            items.Clear();
            for (int i = 1; i < MetadataSharedMethods.ModelsCount + 1; i++)
            {
                items.Add(MetadataSharedMethods.GetModelKindNameByIndex(i));
            }
            if (_editingModel.ModelType != ModelType.None)
            {
                KindBox.SelectedIndex = (int)_editingModel.ModelType - 1;
            }
            _modelinfo.ModelType = _editingModel.ModelType;
            items = GapsFillingMethodBox.Items;
            items.Clear();
            for (int i = 1; i < 2; i++)
            {
                items.Add(MetadataSharedMethods.GetMissingDataMethodNameByIndex(i));
            }
            string mdm = string.Empty;
            if (_editingModel.TryGetParameterByName(Model.MissingDataMethodParameter, ref mdm))
            {
                GapsFillingMethodBox.SelectedIndex = int.Parse(mdm, CultureInfo.CurrentCulture) - 1;
                //_modelinfo.GapsFillingMethod = (MissingDataMethod)int.Parse(mdm, CultureInfo.CurrentCulture);
            }
            else
            {
                GapsFillingMethodBox.SelectedIndex = 0;
                //_modelinfo.GapsFillingMethod = MissingDataMethod.Average;
            }            
            _starting = false;
            RefreshResults();
        }

        /// <summary>
        /// Пересчет результатов модели
        /// </summary>
        private void RefreshResults()
        {            
            //TODO (с использованием проекта Statistics)
            //...
        }

        /// <summary>
        /// Добавление входных рядов
        /// </summary>
        private void AddInSeries()
        {
            ListBox.SelectedObjectCollection items = AllSeriesBox.SelectedItems;
            foreach(object item in items)
            {
                InSeriesBox.Items.Add(item as string);
                _modelinfo.InSeriesWorld.Add(
                    new TimeSeriesWorld(
                        _project.RepositoryLink.GetOrSetWorldKeyByName(Repository.WorldsDefaultName),
                        _project.RepositoryLink.GetObjectDescriptorByLabel(item as string, MetadataObjectKind.TimeSeries) as ITimeSeries
                        )
                        );                
            }
            RefreshResults();
        }

        /// <summary>
        /// Убирает выделенные входные ряды
        /// </summary>
        private void RemoveInSeries()
        {
            _modelinfo.InSeriesWorld.RemoveAt(InSeriesBox.SelectedIndex);
            InSeriesBox.Items.RemoveAt(InSeriesBox.SelectedIndex);
            RefreshResults();
        }

        /// <summary>
        /// Меняет выходной ряд
        /// </summary>
        private void ChangeOutSeries()
        {
            if (!_starting)
            {                
                _modelinfo.OutSeriesWorld.TimeSeries = _project.RepositoryLink.GetObjectDescriptorByLabel(OutSeriesBox.SelectedItem as string, MetadataObjectKind.TimeSeries) as ITimeSeries;
                RefreshResults();
            }
        }

        /// <summary>
        /// Меняет мир выделенного входного ряда
        /// </summary>
        private void ChangeSelectedInSeriesWorld()
        {
            if (!_starting)
            {
                for (int i = 0; i < _modelinfo.InSeriesWorld.Count; i++)
                {
                    ITimeSeries inser = _modelinfo.InSeriesWorld[i].TimeSeries;
                    if (inser.Label == InSeriesBox.SelectedItem as string)
                    {
                        _modelinfo.InSeriesWorld[i].WorldKey = _project.RepositoryLink.GetOrSetWorldKeyByName(InSeriesWorldBox.SelectedItem as string);
                        break;
                    }
                }
                RefreshResults();
            }
        }

        /// <summary>
        /// Меняет мир выходного ряда
        /// </summary>
        private void ChangeOutSeriesWorld()
        {
            if (!_starting)
            {
                _modelinfo.OutSeriesWorld.WorldKey = _project.RepositoryLink.GetOrSetWorldKeyByName(OutSeriesWorldBox.SelectedItem as string);
                RefreshResults();
            }
        }

        /// <summary>
        /// Меняет метод заполнения пропусков
        /// </summary>
        private void ChangeGapsFillingMethod()
        {
            _modelinfo.GapsFillingMethod = (MissingDataMethod)GapsFillingMethodBox.SelectedIndex + 1;
            if (!_starting)
            {                
                RefreshResults();
            }
        }

        /// <summary>
        /// Меняет вид модели
        /// </summary>
        private void ChangeModelKind()
        {
            if (!_starting)
            {
                _modelinfo.ModelType = (ModelType)KindBox.SelectedIndex + 1;
                RefreshResults();
            }
        }

        /// <summary>
        /// Актуализирует комбик мира входного ряда
        /// </summary>
        private void UpdateInSeriesWorld()
        {
            if (!_starting)
            {
                ListBox.SelectedIndexCollection items = InSeriesBox.SelectedIndices;
                if (items.Count == 0)
                {
                    InSeriesWorldBox.Items.Clear();
                    InSeriesWorldBox.Enabled = false;
                    RemoveInSeriesButton.Enabled = false;
                }
                else
                {
                    RemoveInSeriesButton.Enabled = true;
                    ReadWorlds(InSeriesWorldBox);
                    InSeriesWorldBox.Enabled = true;
                    int c = _editingModel.GetInSeriesCount();
                    for (int i = 0; i < c; i++)
                    {
                        if (_editingModel.GetInSeriesByIndex(i).Label == (InSeriesBox.Items[items[i]] as string))
                        {
                            //нашли наш ряд,
                            //смотрим какой у него ключ
                            InSeriesWorldBox.SelectedItem = _project.RepositoryLink.GetWorldNameByKey(_editingModel.GetInSeriesWorldKeyByIndex(items[i]));
                        }
                    }                    
                }
            }
        }

        /// <summary>
        /// Обновляет возможность добавления входных рядов
        /// </summary>
        private void UpdateAddInSeriesButtonAbility()
        {
            AddInSeriesButton.Enabled = (AllSeriesBox.SelectedItems.Count > 0);
        }
        
        /// <summary>
        /// Применение изменений в модели и закрытие диалога
        /// </summary>
        private void ApplyAndClose()
        {
            if (_modelinfo.ModelType == ModelType.None)
            {
                GeneralMethods.ShowUserError(Resources.BI_PROJECTEXPLORER_MODELEDITOR_SELECTKIND);
                DialogResult = DialogResult.None;
                return;
            }
            _editingModel.SetModelType(_modelinfo.ModelType);
            if (_modelinfo.OutSeriesWorld != null)
                _editingModel.SetOutSeries(_modelinfo.OutSeriesWorld.TimeSeries, _modelinfo.OutSeriesWorld.WorldKey);
            _editingModel.SetParameter(Model.MissingDataMethodParameter, ((int)_modelinfo.GapsFillingMethod).ToString());
            _editingModel.ClearInSeries();
            for (int i = 0; i < _modelinfo.InSeriesWorld.Count; i++)
            {
                _editingModel.AddInSeries(_modelinfo.InSeriesWorld[i].TimeSeries, _modelinfo.InSeriesWorld[i].WorldKey);
            }
            DialogResult = DialogResult.OK;
            Close();
        }

        public ModelEditorDialog(ProjectDescriptor currentProject, IModel editingModel)
        {
            _project = currentProject;
            _editingModel = editingModel;
            InitializeComponent();
        }

        private void ModelEditorDialog_Load(object sender, EventArgs e)
        {
            Initialize();
        }

        private void OKButton_Click(object sender, EventArgs e)
        {
            ApplyAndClose();
        }

        private void KindBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeModelKind();
        }

        private void GapsFillingMethodBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeGapsFillingMethod();
        }

        private void AddInSeriesButton_Click(object sender, EventArgs e)
        {
            AddInSeries();
        }

        private void RemoveInSeriesButton_Click(object sender, EventArgs e)
        {
            RemoveInSeries();
        }

        private void InSeriesBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateInSeriesWorld();
        }

        private void OutSeriesBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeOutSeries();
        }

        private void InSeriesWorldBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeSelectedInSeriesWorld();
        }

        private void OutSeriesWorldBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeOutSeriesWorld();
        }

        private void AllSeriesBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateAddInSeriesButtonAbility();
        }
    }
}
